<?php
/**
 * Created by PhpStorm.
 * User: Qch
 * Date: 2016/8/14
 * Time: 9:06
 */

namespace J\Util;

use J\IAsync;
use J\IConfigurable;
use J\Storage\ArrayCache;


/**
 * 配置类基类
 */
class Config extends ArrayCache implements \ArrayAccess, IConfigurable
{
    /**
     * All of the configuration items.
     *
     * @var array
     */
    protected $items = [];

    protected $modified = false;

    /**
     * @var IConfigurable|null
     */
    protected $upper;

    /**
     * @var IAsync
     */
    protected $holder;

    /**
     * Config constructor.
     *
     * @param IConfigurable $upper
     * @param IAsync|array $holder
     */
    public function __construct($upper, $holder = [])
    {
        $this->upper = $upper;

        if ($holder instanceof IAsync) {
            $this->holder = $holder;
        } else {
            $this->items = $holder;
        }

    }

    /**
     * Get all of the configuration items for the application.
     *
     * @return array
     */
    public function all()
    {
        return $this->items;
    }

    /**
     * Determine if the given configuration option exists.
     *
     * @param  string $key
     * @return bool
     */
    public function offsetExists($key)
    {
        return $this->has($key);
    }

    /**
     * Set a configuration option.
     *
     * @param  string $key
     * @param  mixed $value
     * @return void
     */
    public function offsetSet($key, $value)
    {
        $this->set($value, $key);
    }

    /**
     * Get a configuration option.
     *
     * @param  string $key
     * @return mixed
     */
    public function offsetGet($key)
    {
        return $this->get($key);
    }

    /**
     * Unset a configuration option.
     *
     * @param  string $key
     * @return void
     */
    public function offsetUnset($key)
    {
        $this->set(null, $key);
    }

    /**
     * Determine if the given configuration value exists.
     *
     * @param  string $key
     * @return bool
     */
    public function has($key)
    {
        return array_has($this->items, $key);
    }

    /**
     * Get the specified configuration value.
     *
     * @param string $key
     * @param mixed $default
     * @param bool $withUpper
     *
     * @return mixed
     */
    public function get($key, $default = null, $withUpper = true)
    {
        if ($this->has($key))
            return array_get($this->items, $key, $default);

        if (!$withUpper or !$this->upper)
            return $default;

        return $this->upper->get($key, $default);

    }

    /**
     * Set a given configuration value.
     *
     * @param  array|string $key
     * @param  mixed $value
     * @return void
     */
    public function set($value, $key= null)
    {
        if (is_array($key)) {
            foreach ($key as $innerKey => $innerValue) {
                $this->set($innerValue, $innerKey);
            }
            return;
        }

        if ($value === null) {
            $this->modified = array_forget($this->items, $key) or $this->modified;
            return;
        }

        $this->modified = array_set($this->items, $key, $value) or $this->modified;

    }

    public function load()
    {
        if ($this->holder) {
            $values = $this->holder->load();
            if (!is_array($values)) $values = [];
            $this->items = $values;
        }

        $this->modified = false;
    }

    public function save()
    {
        if ($this->modified && $this->holder) {
            $this->holder->save($this->items);
        }
        $this->modified = false;
    }
}